Phase-1 tenant-partitioned aggregate matrix: exclusive/empty/MartenOps-tenant/DeliveryOptions/AlwaysEnforceConsistency + natural-key (#3021)#3033
Merged
Conversation
…no-write, MartenOps tenant overloads, DeliveryOptions routing, AlwaysEnforceConsistency (#3021) Test-only. Extends the Conjoined + Quick + UseTenantPartitionedEvents single-store fixture (string identity) with the remaining single-store aggregate-handler scenarios that persist without AutoApplyTransactions: - exclusive-write concurrency ([AggregateHandler(ConcurrencyStyle.Exclusive)] -> FetchForExclusiveWriting) appends to the routed tenant and stays isolated - empty handler result makes no write (stream + aggregate unchanged) - MartenOps.Store / Insert / Delete tenant overloads land in the targeted tenant's document partition vs default, invoked with no ambient tenant - DeliveryOptions{TenantId} routes the message to that tenant's partition - AlwaysEnforceConsistency detects a concurrent write on the same (tenant) stream -> ConcurrencyException The IEventStream<T> handler-parameter return shape is intentionally omitted: a compound handler that loads an IEventStream<T> via FetchForWriting and appends gets no SaveChanges without AutoApplyTransactions (silently dropped), unlike [AggregateHandler]/IMartenOp returns. Filed as #3032. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Test-only. Reuses the NkOrderAggregate / NkHandlerOrderNumber / NkOrderHandler types from natural_key_aggregate_handler_workflow.cs against the partitioned fixture (Guid stream identity, string natural key). Pins that: - the same natural-key value in two tenants resolves to the routed tenant's own stream (FetchForWriting<T, TNaturalKey> is scoped to the tenant partition, not cross-tenant) - multi-event returns append to the routed tenant - the [WriteAggregate] IEventStream<T> handler (CompleteNkOrder) appends to the routed tenant and stays isolated -- and persists without AutoApplyTransactions, confirming the aggregate-workflow IEventStream path is unaffected by the compound-loader gap (#3032) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Closed
17 tasks
This was referenced Jun 8, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Advances #3021 Phase 1 — test-only, no version bump. Extends the single-store Conjoined + Quick +
UseTenantPartitionedEventsfixture with the remaining aggregate-handler scenarios that don't need a new harness.New coverage
tenant_partitioned_aggregate_matrix_phase1b(string identity):[AggregateHandler(ConcurrencyStyle.Exclusive)]→FetchForExclusiveWriting; appends to the routed tenant, stays isolated.MartenOpstenant overloads —Store/Insert/Delete(doc, tenantId)land in the targeted tenant's document partition vs default, invoked with no ambient tenant.DeliveryOptions{TenantId}routing → correct partition.AlwaysEnforceConsistency— concurrent write on the same tenant stream →ConcurrencyException.tenant_partitioned_natural_key_aggregate(Guid stream identity, string natural key — reusesNkOrderAggregate/NkOrderHandler):FetchForWriting<T, TNaturalKey>is tenant-scoped, not cross-tenant).[WriteAggregate] IEventStream<T>handler completes in the routed tenant and stays isolated.Findings
IEventStream<T>viaFetchForWritingand appends gets noSaveChangeswithoutAutoApplyTransactions(silently dropped), inconsistent with[AggregateHandler]/IMartenOp(the latter self-persists since MartenOps.StartStream side-effect is silently dropped under Events.UseTenantPartitionedEvents #3025). Not partitioning-specific (confirmed against a plain store; Bug_225 only passes withAutoApplyTransactions). That shape is intentionally omitted here and annotated in the matrix; the[WriteAggregate] IEventStream<T>aggregate-workflow path (exercised by the natural-keyCompleteNkOrdertest) is unaffected and persists fine.Verification
Still deferred on #3021 Phase 1
Message-member-bound tenant routing (no first-class core attribute found — needs a design call), the HTTP
[Aggregate]matrix (needs an Alba host),IEventStream<T>loader shape (pending #3032), and remaining return shapeIEventStream<T>non-aggregate. Phases 2–3 unchanged.🤖 Generated with Claude Code